/*
File:		EntityManager.cpp
Course:		SGD 1301
Author:		Peter Nguyen
Purpose:	Stores & maintains game entities
*/

#include "EntityManager.h"


#include "IEntity.h"
#include "Entity.h"
#include "Game.h"
#include <cassert>
#include <iostream>
#include <fstream>

////////////////////////////////////////////////////////////
// SINGLETON
//	- instantiate the static data member
/*static*/ CEntityManager* CEntityManager::s_pInstance	= nullptr;

/*static*/ CEntityManager* CEntityManager::GetInstance()
{
	if( s_pInstance == nullptr )
		s_pInstance = new CEntityManager;

	return s_pInstance;
}

tVector2D CEntityManager::GetWayPoint(int &index)
{
	index++;
	if(index < 0)
		return waypoints[waypoints.size()-1];
	int uiSize = waypoints.size();
	if(index >= uiSize)
	{
		index = -1;
		return waypoints[0];
	}
	return waypoints[index];
}

/*static*/ void	CEntityManager::DeleteInstance()
{
	delete s_pInstance;
	s_pInstance = nullptr;
}


////////////////////////////////////////////////////////////
// CONSTRUCTOR
CEntityManager::CEntityManager(void)
{
	m_bIterating = false;
}

////////////////////////////////////////////////////////////
// DESTRUCTOR
CEntityManager::~CEntityManager(void)
{
}


////////////////////////////////////////////////////////////
// AddEntity			- add the entity to the list
//						- Entity Manager holds a reference
void CEntityManager::AddEntity( IEntity* ptr )
{
	// Validate the parameter
	assert( ptr != nullptr && "CEntityManager::AddEntity - parameter is null" );

	// Validate the iteration state
	assert( m_bIterating == false && "CEntityManager::AddEntity - cannot add while iterating" );

	switch(ptr->GetType())
	{
	case CEntity::EntityType::ENT_POWERUP1:
	case CEntity::EntityType::ENT_POWERUP2:
	case CEntity::EntityType::ENT_POWERUP3:
	case CEntity::EntityType::ENT_POWERUP4:
		m_lPowerUp.push_back(ptr);
		ptr->AddRef();
		m_hashTiles.insert(EntityHashPair(ptr->GetHashKey(),ptr));
		ptr->AddRef();
		break;
	case CEntity::EntityType::ENT_TILES:
		m_hashTiles.insert(EntityHashPair(ptr->GetHashKey(),ptr));
		ptr->AddRef();
		break;
	case CEntity::EntityType::ENT_AISHIP:
	case CEntity::EntityType::ENT_BASE:
	case CEntity::EntityType::ENT_LASER:
	case CEntity::EntityType::ENT_INPUTSHIP:
	case CEntity::EntityType::ENT_TURRET:
		m_lEntities.push_back( ptr );
		ptr->AddRef();
		break;
	default:
		assert( false && "CEntityManager::AddEntity - type isn't defined" );
		break;
	}
}

void	CEntityManager::LoadWayPoints(int _tileSize)
{
	std::ifstream myFile;
	myFile.open("waypoint2.txt");
	if(myFile.is_open())
	{
		int x,y;
		//std::getline(myFile,str);
		while(!myFile.eof())
		{
			myFile >> x;
			myFile.get();
			myFile >> y;
			myFile.get();
			tVector2D p = {x*_tileSize+_tileSize/2.0f,y*_tileSize+_tileSize/2.0f};
			waypoints.push_back(p);
		}
		waypointMax = waypoints.size();
		myFile.close();
	}
}

unsigned int CEntityManager::GetWayPointSize()
{
	return waypoints.size();
}
////////////////////////////////////////////////////////////
// RemoveEntity			- remove the entity from the list
//						- Entity Manager will release the reference
void CEntityManager::RemoveEntity( IEntity* ptr )
{
	// Validate the parameter
	assert( ptr != nullptr && "CEntityManager::RemoveEntity - parameter is null" );

	// Validate the iteration state
	assert( m_bIterating == false && "CEntityManager::RemoveEntity - cannot remove while iterating" );

	// Find & remove the entity
	for( EntityList::iterator iter = m_lEntities.begin();
		iter != m_lEntities.end(); ++iter )
	{
		if( (*iter) == ptr )
		{
			// Found it!
			m_lEntities.erase( iter );
			ptr->Release();
			break;
		}
	}
}

////////////////////////////////////////////////////////////
// RemoveAll			- release the references of all entities
void CEntityManager::RemoveAll( void )
{
	assert( m_bIterating == false && "CEntityManager::RemoveAll - cannot remove all while iterating" );
	
	for( EntityList::iterator iter = m_lEntities.begin();iter != m_lEntities.end(); ++iter )
		(*iter)->Release();
	m_lEntities.clear();

	for( EntityList::iterator iter = m_lPowerUp.begin();iter != m_lPowerUp.end(); ++iter )
		(*iter)->Release();
	m_lPowerUp.clear();

	for( EntityHashmap::iterator iter = m_hashTiles.begin();iter != m_hashTiles.end(); ++iter )
		iter->second->Release();
	m_hashTiles.clear();
}

////////////////////////////////////////////////////////////
// UpdateAll			- update each entity
void CEntityManager::UpdateAll( float fElapsedTime )
{
	// Lock the iterator
	m_bIterating = true;

	// Call each entity's Update method	
	for( EntityList::iterator iter = m_lEntities.begin();
		iter != m_lEntities.end(); ++iter )
	{
		(*iter)->Update( fElapsedTime );
	}

	for( EntityList::iterator iter = m_lPowerUp.begin();
		iter != m_lPowerUp.end(); ++iter )
	{
		(*iter)->Update( fElapsedTime );
	}

	// Unlock the iterator
	m_bIterating = false;
}

////////////////////////////////////////////////////////////
// RenderAll			- render each entity
void CEntityManager::RenderAll( void )
{
	// Lock the iterator
	m_bIterating = true;

	CGame*gamePtr=CGame::GetInstance();

	for( EntityList::iterator iter = m_lEntities.begin();
		iter != m_lEntities.end(); ++iter )
	{
		(*iter)->Render();
	}

	int boundWEST = gamePtr->GetCameraX();
	int boundEAST = gamePtr->GetCameraX()+gamePtr->GetWidth();
	int boundNORTH= (int)(gamePtr->GetCameraY()+gamePtr->GetHeight()*0.5f-gamePtr->GetWidth()*0.5f);
	int boundSOUTH= gamePtr->GetCameraY()+gamePtr->GetWidth();

	int scale = 7*16;
	boundWEST= (boundWEST/scale)-2;
	boundEAST= (boundEAST/scale)+2;
	boundNORTH=(boundNORTH/scale)-1;
	boundSOUTH=(boundSOUTH/scale);

	for(int startX = boundWEST;startX<boundEAST;++startX)
	{
		for(int startY = boundNORTH;startY<boundSOUTH;++startY)
		{
			EntityHashmap::iterator m_iter = m_hashTiles.find(startY*128+startX);
			if(m_iter!=m_hashTiles.end())
			{
				m_iter->second->Render();
			}
		}	
	}









	// Unlock the iterator
	m_bIterating = false;
}


////////////////////////////////////////////////////
// CheckAllCollisions	- check each entity for collision against 
//						  entities of a different type
void CEntityManager::CheckAllCollisions( void )
{
	// Lock the iterator
	m_bIterating = true;

	CGame*gamePtr=CGame::GetInstance();

	// Check each entity against the others
	for( EntityList::iterator iterI = m_lEntities.begin(); iterI != m_lEntities.end(); ++iterI )
	{
		for( EntityList::iterator iterJ = m_lEntities.begin(); iterJ != m_lEntities.end(); ++iterJ )
		{
			// If they are different types
			if( (*iterI) != (*iterJ))
			{
				// Check for collision
				(*iterI)->CheckCollision( *iterJ );
			}
		}
	}

	for( EntityList::iterator iterJ = m_lEntities.begin(); iterJ != m_lEntities.end(); ++iterJ )
	{
		{
			CEntity* entPtr = (CEntity*)(*iterJ);

			int boundWEST= (int)entPtr->GetPosX()-entPtr->GetWidth();
			int boundEAST= (int)entPtr->GetPosX()+entPtr->GetWidth();
			int boundNORTH=(int)entPtr->GetPosY()-entPtr->GetHeight();
			int boundSOUTH=(int)entPtr->GetPosY()+entPtr->GetHeight();

			int scale = 7*16;
			boundWEST= (boundWEST/scale)-1;
			boundEAST= (boundEAST/scale)+1;
			boundNORTH=(boundNORTH/scale)-1;
			boundSOUTH=(boundSOUTH/scale)+1;

			for(int startX = boundWEST;startX<boundEAST;++startX)
			{
				for(int startY = boundNORTH;startY<boundSOUTH;++startY)
				{
					EntityHashmap::iterator m_iter = m_hashTiles.find(startY*128+startX);
					if(m_iter!=m_hashTiles.end())
					{
						m_iter->second->CheckCollision(*iterJ);
					}
				}	
			}
		}
	}
	// Unlock the iterator
	m_bIterating = false;
}
